package com.lee.forum.ws;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lee.forum.ForumApplication;
import com.lee.forum.config.AppConfig;
import com.lee.forum.model.Message;
import com.lee.forum.model.User;
import com.lee.forum.model.extentsClass.WsMessage;
import com.lee.forum.mq.publisher.MqPoster;
import com.lee.forum.utils.MessageUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


@Component
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfigurator.class)
public class ChatEndpoint {

    // 用来存储每一个客户端对象对应的ChatEndpoint对象
    public static ConcurrentHashMap<User, ChatEndpoint> onlineClient = new ConcurrentHashMap<>();
    // 声明session对象，通过该对象可以发生消息给指定的客户端
    private Session session;
    // 声明Httpsession对象，里面记录了用户相关信息
    private HttpSession httpSession;

    /**
     * 连接建立时调用
     *
     * @param session 当前连接的会话
     * @param config  Endpoint的配置对象
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        try {
            // 初始化当前Endpoint的链接会话
            this.session = session;
            // 初始化httpSession
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            this.httpSession = httpSession;
            // 将当前连接的ChatEndpoint对象存储到onlineClient容器中
            User user = (User) httpSession.getAttribute(AppConfig.USER_SESSION_KEY);
            onlineClient.put(user, this);
            // 将在线用户推送给所有客户端
            // 1.获取消息
            String message = MessageUtils.getMessage(true, false, null, user.getNickname());
            // 2.将消息广播给所有用户
            broadcastAllUsers(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void broadcastAllUsers(String message) {
        Set<User> users = onlineClient.keySet();
        try {
            for (User user : users) {
                // 获取当前用户的ChatEndpoint对象
                ChatEndpoint client = onlineClient.get(user);
                // 使用session发送信息
                client.session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

//    private Set<String> getNames() {
//        Set<String> res = new HashSet<>();
//        for (User user : onlineClient.keySet()) {
//            res.add(user.getNickname());
//        }
//        return res;
//    }

    /**
     * 接收到客户端数据后调用
     *
     * @param message 消息信息
     * @param session 当前连接的会话
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        // 导入jackson核心类
        ObjectMapper objectMapper = new ObjectMapper();
        // 将JSON字符串序列化为Message对象
        WsMessage mess = objectMapper.readValue(message, WsMessage.class);
        // 取出接收人
        String toName = mess.getToName();
        // 取出接收人id
        Long id = mess.getId();
        // 取出信息
        String sendMess = mess.getMessage();
        // 找到发送人姓名
        User user = (User) httpSession.getAttribute(AppConfig.USER_SESSION_KEY);
        String fromName = user.getNickname();
        // 进行消息构造
        String finalMess = MessageUtils.getMessage(false, false, fromName, sendMess);
        // 根据接收人找到对应的连接
        User receiver = null;
        for (User user1 : onlineClient.keySet()) {
            // 根据接收到的接收人id找到对应的ChatEndpoint端点的key值
            if (user1.getId() == id) {
                receiver = user1;
                break;
            }

        }

        ChatEndpoint chatEndpoint = null;
        if (receiver != null) {
             chatEndpoint = onlineClient.get(receiver);
        }
        if (chatEndpoint == null) {
            // 这里可以抛出异常
            // ...
            // 打印日志
            System.out.println("找不到对应的客户端！userName = " + toName);
//            return;
        } else {
            // 使用对应的会话Session发送给对应的客户端
            chatEndpoint.session.getBasicRemote().sendText(finalMess);
        }


        // 使用RabbitMQ异步方式进行消息的存储
        // 4.封装站内信对象
        Message realTimeMessage = new Message();
        realTimeMessage.setPostUserId(user.getId()); // 设置发送者Id
        realTimeMessage.setReceiveUserId(id); // 设置接收者Id
        realTimeMessage.setContent(sendMess); // 设置站内信内容
        // 设置状态
        if (chatEndpoint == null) {
            realTimeMessage.setState((byte) 0); // 设置未读
        } else {
            realTimeMessage.setState((byte) 1); // 设置已读
        }
        // 设置创建更新时间
        Date date = new Date();
        realTimeMessage.setCreateTime(date);
        realTimeMessage.setUpdateTime(date);

        // 不能注入获取，只能通过spring上下文获取
        MqPoster mqPoster = ForumApplication.context.getBean(MqPoster.class);
        // 将消息投递到MQ
        mqPoster.postRealTimeMessage(realTimeMessage);
//        IMessageService messageService = ForumApplication.context.getBean(IMessageService.class);
//
//        // 5.调用Service发送站内信
//        messageService.createRealTime(realTimeMessage);
    }

    /**
     * 连接过程中发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("websocket连接出错，错误原因:" + error);
        error.printStackTrace();
    }

    /**
     * 关闭连接时调用
     */
    @OnClose
    public void onClose(Session session) {
        try {
            // 1.清除容器中的该ChatEndpoint对象
            User user = (User) httpSession.getAttribute(AppConfig.USER_SESSION_KEY);
            onlineClient.remove(user);
            // 2.向所有客户端广播下线通知
//            Set<String> names = getNames();
            // 3.构造消息
            String message = MessageUtils.getMessage(true, true, null, user.getNickname());
            // 4.广播给其他用户
            broadcastAllUsers(message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
